use std::{cmp::max, collections::HashMap, future::Future, path::PathBuf, time::Duration};

#[cfg(feature = "ser")]
use std::{fs::File, fmt::{Debug, Display}, hash::Hash, io::{BufRead, BufReader}, path::Path};

#[cfg(feature = "ser")]
#[cfg(feature = "cmd")]
use std::collections::HashSet;

use chrono::Local;

#[cfg(feature = "ser")]
use clap::{Args, Parser};

#[cfg(feature = "ser")]
use serde::{Deserialize, Serialize};

#[cfg(feature = "random")]
use uuid::Uuid;

#[cfg(feature = "net")]
use sysinfo::System;
#[cfg(feature = "net")]
use tokio::{runtime::{Builder, Runtime}, signal, spawn, task::JoinHandle, time::sleep};

#[cfg(feature = "encrypt")]
use crate::encrypt::{encrypt, WTASK_KEY};
#[cfg(feature = "encrypt")]
use local_ip_address::local_ip;

#[cfg(feature = "ser")]
#[cfg(feature = "cmd")]
use crate::cmd::create_file;

#[cfg(feature = "net")]
use crate::export::{kill_channel, KillReceiver, KillSender};

#[cfg(feature = "ser")]
use crate::{error::WError, logs::LogArgs};

use super::{error::WResult, app::get_wtask_root_dir};

pub static WTASK_MACHINE_CONFIG_FILE: &str = "wtask.config.jsonl";
pub static WTASK_CLI_CONFIG_FILE: &str = "wtask.config.cli.jsonl";
pub static WTASK_MACHINE_RUNNING_FILE: &str = "wtask.machine.running";


#[cfg(feature = "random")]
pub fn random_token() -> String {
    Uuid::new_v4().to_string()
}

#[cfg(unix)]
unsafe extern "system" {
    fn geteuid() -> u32;
}

#[cfg(target_os = "windows")]
unsafe fn geteuid() -> u32 {
    get_user_name().as_bytes().iter().map(| v | *v as u32).sum()
}


#[cfg(feature = "encrypt")]
pub fn get_machine_token() -> WResult<String> {
    let t = format!(
        "{}@{}@{}@{}",
        get_user_name(),
        System::host_name().unwrap().replace(" ", ""),
        local_ip().unwrap(),
        System::name().unwrap().replace(" ", ""),
    );
    // todo: encrypt
    let words = "ABCDEFGHIJKLMNOPQRSTUVWXYZ123456";
    let res = encrypt(t.as_bytes(), WTASK_KEY)?
        .into_iter()
        .map(| v |{
            let i = v as usize % words.len();
            words.get(i..i+1).unwrap()
        })
        .collect::<String>();
    Ok(res)
}


pub fn get_machine_name(machine_token: &str) -> String {
    let m = max(1, machine_token.len() / 8);
    let mm = (m - 1) / 2;
    machine_token.chars()
            .enumerate()
            .filter(|(i, _)| (i % m) == mm)
            .map(| (_, v) | v)
            .collect::<String>()
}


pub fn get_user_name() -> String {
    std::env::var("USERNAME").or_else(|_| std::env::var("USER")).unwrap_or("user".to_string())
}


pub fn get_file_maching_running() -> PathBuf {
    get_wtask_root_dir().join(WTASK_MACHINE_RUNNING_FILE)
}


#[cfg(feature = "net")]
pub async fn wait_machine_ready(limit_memory: u8, limit_cpu: u8) -> WResult<()> {
    let mut sys = System::new();
    let limit_memory = limit_memory as f32 / 100.0;
    let limit_cpu = limit_cpu as f32 / 100.0;
    loop {
        // 判断机器资源
        sys.refresh_cpu_usage();
        sys.refresh_memory();
        let memory_usage = (sys.used_memory() / sys.total_memory()) as f32 + 0.01;
        let cpu_usage = sys.cpus().iter().map(| x | x.cpu_usage()).sum::<f32>() / sys.cpus().len() as f32;
        log::debug!("cpu: {cpu_usage}, memory: {memory_usage}");
        if memory_usage > limit_memory || cpu_usage > limit_cpu {
            sleep(Duration::from_secs(10)).await;
            continue;
        }
        break;
    }
    Ok(())
}


#[cfg(feature = "net")]
pub struct WSpawn<F>
where
    F: Future + Send + 'static,
    F::Output: Send + 'static,
{
    s: usize,
    i: usize,
    data: Vec<JoinHandle<F::Output>>
}


#[cfg(feature = "net")]
impl<F> WSpawn<F>
where
    F: Future + Send + 'static,
    F::Output: Send + 'static,
{
    pub fn new(s: usize) -> Self {
        Self { s, i: 0, data: Vec::new() }
    }

    pub fn catch_pause_signal<FF>(&self, future: FF) -> (KillSender<bool>, KillReceiver<bool>)
    where
        FF: Future + Send + 'static,
        FF::Output: Send + 'static,
    {
        let (kill_tx, kill_rx) = kill_channel(false);
        let kill_tx_clone = kill_tx.clone();
        spawn(async move {
            let _ = signal::ctrl_c().await;
            let _ = kill_tx.send(true);
            let _ = future.await;
        });
        (kill_tx_clone, kill_rx)
    }

    pub fn spawn(&mut self, future: F) {
        let t = spawn(future);
        if self.data.len() < self.s {
            // 有空位
            self.data.push(t);
        } else {
            // 没有空位
            // 现在的位置就是最早的位置
            self.i %= self.s;
            self.data[self.i].abort();
            self.data[self.i] = t;
        }
        self.i += 1;
    }
}


#[cfg(feature = "net")]
impl<F> Default for WSpawn<F>
where
    F: Future + Send + 'static,
    F::Output: Send + 'static,
{
    fn default() -> Self {
        Self::new(2048)
    }
}

#[cfg(feature = "net")]
impl<F> Drop for WSpawn<F>
where
    F: Future + Send + 'static,
    F::Output: Send + 'static,
{
    fn drop(&mut self) {
        for t in &self.data {
            t.abort();
        }
    }
}



pub struct WSession<T> {
    data: HashMap<String, T>,
    keys: Vec<Option<String>>,
    keys_index: HashMap<String, usize>,
    time: HashMap<String, i64>,
    s: usize,
}

impl<T> WSession<T> {
    pub fn new(s: usize) -> Self {
        Self {
            data: HashMap::with_capacity(s),
            keys: Vec::with_capacity(s),
            keys_index: HashMap::with_capacity(s),
            time: HashMap::with_capacity(s),
            s
        }
    }

    fn next_index(&mut self) -> usize {
        let keys_index_len = self.keys_index.len();
        let keys_len = self.keys.len();
        if keys_index_len < self.s {
            // 有空位
            if keys_len == keys_index_len {
                // 中间没有空
                self.keys.push(None);
                keys_len
            } else {
                // 中间空了，即中间有None
                self.keys
                    .iter()
                    .enumerate()
                    .find(| (_, v) | v.is_none())
                    .unwrap().0
            }
        } else {
            // 没有空位
            // 删除第一个
            let key_remove = self.keys[0].take().unwrap();
            self.keys_index.remove(&key_remove);
            self.data.remove(&key_remove);
            0
        }
    }

    pub fn insert(&mut self, key: &str, value: T) {
        let index = self.next_index();
        self.data.insert(key.to_string(), value);
        self.time.insert(key.to_string(), Local::now().timestamp());
        self.keys_index.insert(key.to_string(), index);
        self.keys[index].replace(key.to_string());
    }

    pub fn get(&self, key: &str) -> Option<&T> {
        self.data.get(key)
    }

    pub fn contains(&self, key: &str) -> bool {
        self.data.contains_key(key)
    }

    pub fn get_mut(&mut self, key: &str) -> Option<&mut T> {
        self.data.get_mut(key)
    }

    pub fn remove(&mut self, key: &str) -> Option<T> {
        if !self.keys_index.contains_key(key) {
            return None;
        }
        let res = self.data.remove(key);
        let _ = self.time.remove(key);
        let index_remove = self.keys_index.remove(key).unwrap();
        self.keys[index_remove].take().unwrap();
        res
    }

    pub fn clean(&mut self, expire: i64) {
        let time_expire = Local::now().timestamp() - expire;
        let ks = self.time.iter()
            .filter(| v | v.1 < &time_expire)
            .map(| v | v.0.clone())
            .collect::<Vec<_>>();
        
        if ks.is_empty() {
            return;
        }

        for k in ks {
            let _ = self.remove(&k);
        }
    }
}

impl<T> Default for WSession<T> {
    fn default() -> Self {
        Self::new(10240)
    }
}


#[cfg(feature = "ser")]
#[derive(Clone, Args, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub struct CMDArgsConfigMachine {
    /// 如果为本地服务的端口，为0则将用户UID作为端口号，如果用户UID小于100，则为2589；如果是网桥代理，为0则为默认端口2560
    #[arg(long, default_value = "0")]
    pub machine_port: u32,
    
    #[arg(long, default_value = "0.0.0.0")]
    pub machine_ip: String,

    /// 是否是网桥代理
    #[arg(long)]
    pub proxy: bool,

    /// 连接machine的token
    #[arg(long)]
    pub machine_token: String,
}


#[cfg(feature = "ser")]
impl Display for CMDArgsConfigMachine {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "{}@{}:{}",
            get_machine_name(&self.machine_token),
            self.machine_ip,
            self.machine_port
        )
    }
}

#[cfg(feature = "ser")]
impl Debug for CMDArgsConfigMachine {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self)
    }
}


#[cfg(feature = "ser")]
#[derive(Debug, Clone, Parser, Serialize, Deserialize)]
pub struct CMDMachineArgs {
    /// 从配置文件加载配置
    #[arg(long)]
    pub file_config: Option<String>,
    
    /// 默认不管是通过本地连接还是远程连接均为相同的token。如果不给定该token，则随机生成。如果给定本地或远程token，则对应的token强制设定为给定的，此时两种访问方式的token可能不一样。
    #[arg(long)]
    pub server_token: Option<String>,

    /// 本地服务端口配置，为0则将用户UID作为端口号，如果用户UID小于100，则为2589；如果是网桥代理，为0则为默认端口2560
    #[arg(long, default_value = "0")]
    pub local_port: u32,

    /// 本地服务器授权token，不给定token则随机生成
    #[arg(long)]
    pub local_machine_token: Option<String>,

    #[arg(long, default_value = "2560")]
    pub proxy_port: u32,
    
    #[arg(long, default_value = "120.26.192.195")]
    pub proxy_ip: String,

    /// 代理服务器授权token，不给定token则随机生成
    #[arg(long)]
    pub proxy_machine_token: Option<String>,

    #[command(flatten)]
    pub log_args: LogArgs,
}



pub fn machine_port(mut port: u32, proxy: bool) -> u32 {
    if port == 0 {
        if proxy {
            port = 2560;
        } else {
            let mut uid = unsafe { geteuid() };
            if uid < 1024 {
                uid += 2589;
            }
            port = uid;
        }
    }
    port
}

pub fn get_home_dir() -> PathBuf {
    let res = std::env::var("HOME").unwrap_or_else(|_| "/".to_string());
    PathBuf::from(res)
}


#[cfg(feature = "ser")]
fn load_config_from_file_inner(file_config: &Path) -> WResult<Vec<CMDArgsConfigMachine>> {
    if !file_config.exists() {
        return Err(WError::DataError(format!("file config not exist: {file_config:?}")));
    }
    let f = File::open(file_config)?;
    let data = BufReader::new(f)
        .lines()
        .filter_map(| v | {
            if let Ok(v) = v {
                serde_json::from_str(v.as_str()).ok()
            } else {
                None
            }
        })
        .collect::<Vec<_>>();
    Ok(data)
}


#[cfg(feature = "ser")]
pub fn load_config_from_file(file: &str, dir: Option<&str>) -> WResult<Vec<CMDArgsConfigMachine>> {
    let dir = if let Some(dir) = dir {
        PathBuf::from(dir)
    } else {
        get_wtask_root_dir()
    };
    let file_config = dir.join(file);
    load_config_from_file_inner(&file_config)
}


#[cfg(feature = "ser")]
#[cfg(feature = "cmd")]
pub fn write_config_to_file(data: Vec<CMDArgsConfigMachine>, file: &str) -> WResult<()> {
    let file_config = get_wtask_root_dir().join(file);
    let config_old = load_config_from_file_inner(&file_config).unwrap_or_default();
    let mut data_set = HashSet::new();
    data_set.extend(data);
    data_set.extend(config_old);
    let data_set = data_set.iter()
        .filter_map(| v | serde_json::to_string(v).ok())
        .collect::<Vec<_>>();
    let data_str = data_set.join("\n");
    create_file(&file_config, &data_str)?;
    log::info!("save config to file: {:?}, machine num {}", file_config, data_set.len());
    Ok(())
}






#[cfg(feature = "net")]
pub fn runtime_init(n: usize) -> WResult<Runtime> {
    let runtime = if n < 2 {
        Builder::new_current_thread()
            .enable_all()
            .build()
            .unwrap()
    } else {
        Builder::new_multi_thread()
            .worker_threads(n)
            .enable_all()
            .build()
            .unwrap()
    };
    Ok(runtime)
}




#[cfg(feature = "ser")]
#[derive(Debug, Clone, Args, Serialize, Deserialize)]
pub struct CMDArgsConfigSsh {
    #[arg(long)]
    pub ssh_ip: String,

    #[arg(long)]
    pub ssh_user: String,

    #[arg(long, default_value = "22")]
    pub ssh_port: u32,
    
    /// 使用私钥进行身份验证
    #[arg(long)]
    pub private_file: Option<PathBuf>,

    /// 强制安装，不管远程是否运行
    #[arg(long)]
    pub force: bool,

    #[command(flatten)]
    pub config: CMDMachineArgs,
}


#[cfg(feature = "ser")]
#[derive(Debug, Clone, Args, Serialize, Deserialize)]
pub struct CMDArgsConfigFile {
    /// 配置文件所在文件夹，不提供则为用户文件夹下的wtask
    #[arg(long)]
    pub dir: Option<String>,
}



#[cfg(feature = "ser")]
#[derive(Debug, Clone, Parser, Serialize, Deserialize)]
pub enum CMDArgsConfigAll {
    /// 配置代理连接远程服务器
    Machine(CMDArgsConfigMachine),
    /// 配置连接Ssh服务器
    Ssh(CMDArgsConfigSsh),
    /// 根据已有的配置文件加载配置
    File(CMDArgsConfigFile),
    /// 查看已配置信息
    List,
}




#[cfg(feature = "ser")]
pub fn get_config(config: CMDArgsConfigAll) -> WResult<Vec<CMDArgsConfigMachine>> {
    let t = match config {
        CMDArgsConfigAll::File(args_file) => {
            if let Ok(c) = load_config_from_file(WTASK_MACHINE_CONFIG_FILE, args_file.dir.as_deref()) {
                c
            } else {
                load_config_from_file(WTASK_CLI_CONFIG_FILE, args_file.dir.as_deref())?
            }
        },
        CMDArgsConfigAll::Machine(config) => {
            vec![config]
        },
        _ => {
            return Err(WError::DataError("client load config error".to_string()));
        }
    };
    Ok(t)
}

